/***********************************************
 This file is part of redia-library-app-ios.
 
 Copyright (c) 2012, 2013 Redia A/S
 
 Redia-library-app-ios is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 Redia-library-app-ios is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with redia-library-app-ios.  If not, see <http://www.gnu.org/licenses/>.
 
 *********************************************** */


#import "ChannelFetchManager.h"
#import "InfoObject.h"
#import "defines.h"
#import "IGLanguageManager.h"
#import "WeakReferenceHolder.h"

@implementation ChannelFetchManager

//@synthesize projectName;
@synthesize mainElements;
@synthesize allInfoObjects;
@synthesize lastXMLRPCCallTimestamp;
@synthesize createTagIndex;
@synthesize tagsToInfoObjects;
@synthesize forceUseCacheData;
@synthesize tagIndexCaseInsensitive;
@synthesize tagIndexNonstrictDelimiters;

__strong static ChannelFetchManager *sharedSingleton;

+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        sharedSingleton = [ChannelFetchManager new];
    }
}

+ (ChannelFetchManager*)instance
{
	return sharedSingleton;
}


- (id) init
{
	self = [super init];
	if (self != nil) {
		igChannelFetcher = [InfoGalleriXmlRpcClient201009ChannelFetchDelegate new];
		
		//projectName = @"InfoGalleri";
		delegates = [NSMutableArray new];
		self.mainElements = [NSMutableArray new];
		self.allInfoObjects = [NSMutableDictionary new];
		self.lastXMLRPCCallTimestamp = [NSDate distantPast];
		isFetched=FALSE;
		isFetching=FALSE;
        self.createTagIndex=false;
        self.tagsToInfoObjects = [NSMutableDictionary new];
        forceUseCacheData=NO;
        _fetchInfoObjectDelegates = [NSMutableArray new];
	}
	return self;
}
-(void)useNewRediaPublishClient
{
    [igChannelFetcher useNewRediaPublishClient];
}
-(bool)isUsingNewBackend
{
    return [igChannelFetcher isUsingNewBackend];
}

- (void)setCustomerDB:(NSString*)cust withGallery:(NSString*)gall
{
	customerId = cust;
	galleryId = gall;
}

-(void)setFetchCannelsSequentially:(BOOL)sequentially
{
    igChannelFetcher.fetchChannelsSequentially = sequentially;
}

- (void)fetchChannels:(id<ChannelFetchCompleteDelegate>)delegate
{
    [self fetchChannels:delegate usingPackageSystem:NO];
}
-(void)fetchChannelsFromPackage:(id<ChannelFetchCompleteDelegate>)delegate
{
    [self fetchChannels:delegate usingPackageSystem:YES];
}

- (void)fetchChannels:(id<ChannelFetchCompleteDelegate>)delegate usingPackageSystem:(BOOL)use_package
{
    if (![delegates containsObject:delegate]) {
        [delegates addObject:delegate];
    }
    
	if (isFetched && ![self isRefreshNeeded]) {
		[delegate channelFetchComplete:igChannelFetcher.infoObjects];
	} else {
		if (!isFetching) {
            if (!forceUseCacheData) {
                if (use_package) {
                    [igChannelFetcher fetchFromPackageForCustomer:customerId withGallery:galleryId delegate:self];
                } else {
                    [igChannelFetcher fetchGalleryForCustomer:customerId withGallery:galleryId delegate:self];
                }
            } else {
                [igChannelFetcher fetchFromCacheWithDelegate:self];
            }
            isFetching = true;
			isFetched = false;
			[self updateLastCallTimestamp];
		}
	}

}

- (void)fetchInfoObject:(NSString*)infoObjectId delegate:(id<InfoObjectFetchCompleteDelegate>)delegate
{
    // Add the delegate if it is not already there.
    BOOL delegateIsAlreadyThere = NO;
    for (WeakReferenceHolder *refHolder in self.fetchInfoObjectDelegates) {
        if (refHolder.object == delegate) {
            delegateIsAlreadyThere = YES;
        }
    }
    if (delegateIsAlreadyThere == NO) {
        [self.fetchInfoObjectDelegates addObject:[WeakReferenceHolder newWithObject:delegate]];
    }
    
    // Send the XMLRPC request.
    [igChannelFetcher fetchSingleInfoObjectForCustomer:customerId objectId:infoObjectId delegate:self];
}

- (void)unregisterDelegate:(id<ChannelFetchCompleteDelegate>)delegate
{
    [delegates removeObject:delegate];
}

- (void)completedChannels:(int)completedNum ofTotal:(int)totalNum
{
    for (id<ChannelFetchCompleteDelegate> d in delegates) {
        if ([d respondsToSelector:@selector(completedChannels:ofTotal:)]) {
            [d completedChannels:completedNum ofTotal:totalNum];
        }
    }
}

- (NSString *)convertTagUsingStripping:(NSString *)orig_tag
{
    if (orig_tag==nil) {
        return nil;
    }
    if (!tagIndexNonstrictDelimiters && !tagIndexCaseInsensitive) {
        return orig_tag;
    }
    NSMutableString* actual_tag = [orig_tag mutableCopy];
    if (tagIndexCaseInsensitive) {
        actual_tag=[[actual_tag lowercaseString] mutableCopy];   
    }
    if (tagIndexNonstrictDelimiters) {
        //strip all space, hyphen and underscore delimiters
        [actual_tag replaceOccurrencesOfString:@" " withString:@"" options:0 range:NSMakeRange(0, [actual_tag length])];
        [actual_tag replaceOccurrencesOfString:@"-" withString:@"" options:0 range:NSMakeRange(0, [actual_tag length])];
        [actual_tag replaceOccurrencesOfString:@"_" withString:@"" options:0 range:NSMakeRange(0, [actual_tag length])];
    }
    return actual_tag;
}


#pragma mark - ChannelFetchCompleteDelegate

- (void)channelFetchComplete:(NSArray*) infoObjects
{
	isFetched = true;
	isFetching = false;

    NSString* lang = [[IGLanguageManager instance] getCurrentIGLangID];

	self.mainElements = [NSMutableArray new];
	self.allInfoObjects = [NSMutableDictionary new];
    self.tagsToInfoObjects = [NSMutableDictionary new];

	NSMutableSet* info_objs_marked_as_subpages = [NSMutableSet new];
	
	int localid = 0;
	for (InfoObject* inf in infoObjects) {
		NSString* info_id = [inf getId];
		if (info_id==nil || [info_id length]==0) {
			info_id = [NSString stringWithFormat:@"%d",localid];
			DLog(@"added infoobject without id with key %@",info_id);
		}
		[allInfoObjects setValue:inf forKey:info_id];
		
		//now remember all subpages
		for (NSString* child_id in [inf getChildInfoObjects] ) {
			[info_objs_marked_as_subpages addObject:child_id];
		}
		
		//check if this object was previously marked as a subpage.
		//the server guarantees that all subpages appear last in the array
		if (![info_objs_marked_as_subpages containsObject:info_id]) {
			//not marked, so this is a main element
			[mainElements addObject:inf];
		}
        
        /*
        if ([[inf getId] isEqualToString:@"abpmy"]) {
            DLog(@"tags for abpmy %@", [inf getTags:lang]);
        }*/
        
        if (self.createTagIndex) {
            NSArray* tags = [inf getTags:lang];
            if (tags!=nil) {
                for (NSString* newtag in tags) {
                    if ([newtag length]>0) {
                        NSString* actual_tag = [self convertTagUsingStripping:newtag];
                        NSMutableArray* tagged_objs = [tagsToInfoObjects objectForKey:actual_tag];
                        if (tagged_objs==nil) {
                            [tagsToInfoObjects setObject:[NSMutableArray arrayWithObject:inf] forKey:actual_tag];
                        } else {
                            [tagged_objs addObject:inf];
                        }
                    }
                }
            }
            
        }
		
		localid++;
	}
	
	for (id<ChannelFetchCompleteDelegate> obj in delegates) {
		[obj channelFetchComplete:infoObjects];
	}
}

-(void)channelFetchError:(NSError *)error
{
    ALog(@"CHANNEL FETCH ERROR: %@",error);
    for (id<ChannelFetchCompleteDelegate> obj in delegates) {
		[obj channelFetchError:error];
	}
}


#pragma mark - InfoObjectFetchCompleteDelegate

- (void)infoObjectFetchComplete:(InfoObject *)infoObject
{
    // Call all the delegates.
    NSMutableArray *removeTheseObjects = [NSMutableArray new];
    for (WeakReferenceHolder *refHolder in self.fetchInfoObjectDelegates) {
        if (refHolder.object) {
            id<InfoObjectFetchCompleteDelegate> iofDelegate = refHolder.object;
            [iofDelegate infoObjectFetchComplete:infoObject];
        }
        else {
            [removeTheseObjects addObject:refHolder];
        }
    }
    
    // Clean up the delegates list.
    for (WeakReferenceHolder *refHolder in removeTheseObjects) {
        [self.fetchInfoObjectDelegates removeObject:refHolder];
    }
}

- (void)infoObjectFetchFailed:(NSString *)objectId
{
    ALog(@"INFO OBJECT FETCH ERROR: %@", objectId);
    
    // Call all the delegates.
    NSMutableArray *removeTheseObjects = [NSMutableArray new];
    for (WeakReferenceHolder *refHolder in self.fetchInfoObjectDelegates) {
        if (refHolder.object) {
            id<InfoObjectFetchCompleteDelegate> iofDelegate = refHolder.object;
            [iofDelegate infoObjectFetchFailed:objectId];
        }
        else {
            [removeTheseObjects addObject:refHolder];
        }
    }
    
    // Clean up the delegates list.
    for (WeakReferenceHolder *refHolder in removeTheseObjects) {
        [self.fetchInfoObjectDelegates removeObject:refHolder];
    }
}

- (NSArray *)getObjectsForTag:(NSString *)tag
{
    if (tag==nil) {
        return nil;
    }
    NSString* actual_tag = [self convertTagUsingStripping:tag];
    DLog(@"looking up for tag %@", actual_tag);
    return [tagsToInfoObjects objectForKey:actual_tag];
}

- (void)addComment:(NSString *)infoObjectId authorName:(NSString *)authorName comment:(NSString *)comment authorEmail:(NSString *)authorEmail delegate:(id <XMLRPCConnectionDelegate>) d
{
	[[igChannelFetcher getClient] addComment:customerId infoObjectId:infoObjectId authorName:authorName comment:comment authorEmail:authorEmail delegate:d];
}

- (void)addVote:(NSString *)infoObjectId delegate:(id <XMLRPCConnectionDelegate>)d
{
    NSRange separatorPosition = [infoObjectId rangeOfString:@"::"];
    if (separatorPosition.location != NSNotFound) {
        // Remove the prefix.
        infoObjectId = [infoObjectId substringFromIndex:separatorPosition.location + 2];
    }
    
    [[igChannelFetcher getClient] addVote:customerId choiceId:infoObjectId delegate:d];
}

- (void)updateLastCallTimestamp 
{
	self.lastXMLRPCCallTimestamp = [NSDate date];
}

- (BOOL)isRefreshNeeded
{
	NSTimeInterval time_since = -[self.lastXMLRPCCallTimestamp timeIntervalSinceNow];
	if (time_since>ChannelFetchManager_DATA_REFRESH_TIMEOUT && !isFetching) {
		return TRUE;
	}
	return FALSE;
}
@end
